<?php


namespace App\Http\Controllers\Api;

use App\Bus\AuthBus;
use App\Bus\WechatScanBus;
use App\Zesow\Wechat;
use App\Zesow\WechatMini;
use App\Zesow\WechatOpenPlatform;
use EasyWeChat\Kernel\Exceptions\InvalidArgumentException;
use EasyWeChat\Kernel\Exceptions\InvalidConfigException;
use EasyWeChat\Kernel\Exceptions\RuntimeException;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Contracts\Container\BindingResolutionException;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Routing\Redirector;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
use Illuminate\Http\Request;
use App\Constant\CacheConstant;
use App\Businesses\BusinessState;
use App\Constant\FrontendConstant;
use App\Exceptions\ServiceException;
use Illuminate\Support\Facades\Auth;
use Laravel\Socialite\Facades\Socialite;
use App\Services\Base\Services\CacheService;
use App\Services\Base\Interfaces\CacheServiceInterface;
use App\Services\Base\Services\ConfigService;
use App\Services\Member\Services\UserService;
use App\Http\Requests\ApiV2\MobileLoginRequest;
use App\Http\Requests\ApiV2\PasswordLoginRequest;
use App\Services\Member\Services\SocialiteService;
use App\Services\Base\Interfaces\ConfigServiceInterface;
use App\Services\Member\Interfaces\SocialiteServiceInterface;
use App\Services\Member\Interfaces\UserServiceInterface;
use Overtrue\Socialite\Exceptions\AuthorizeFailedException;
use App\Services\Other\Models\WechatPlatform;
use EasyWeChat\OpenPlatform\Server\Guard;
use GuzzleHttp\Client;
use Symfony\Component\HttpKernel\Event\RequestEvent;


class LoginController extends BaseController
{
    /**
     * @var UserService
     */
    protected $userService;


    /**
     * @var ConfigService
     */
    protected $configService;

    /**
     * @var CacheService
     */
    protected $cacheService;

    /**
     * @var SocialiteService
     */
    protected $socialiteService;


    /**
     * LoginController constructor.
     * @param UserServiceInterface $userService
     * @param ConfigServiceInterface $configService
     * @param CacheServiceInterface $cacheService
     * @param SocialiteServiceInterface $socialiteService
     */
    public function __construct(
        UserServiceInterface      $userService,
        ConfigServiceInterface    $configService,
        CacheServiceInterface     $cacheService,
        SocialiteServiceInterface $socialiteService


    )
    {
        $this->userService = $userService;
        $this->configService = $configService;
        $this->cacheService = $cacheService;
        $this->socialiteService = $socialiteService;


    }


    /**
     * @api {post} /api/v2/login/password 密码登录
     * @apiGroup Auth
     * @apiVersion v2.0.0
     *
     * @apiParam {String} mobile 手机号
     * @apiParam {String} password 密码
     *
     * @apiSuccess {Number} code 0成功,非0失败
     * @apiSuccess {Object} data 数据
     * @apiSuccess {String} data.token token
     */
    public function passwordLogin(PasswordLoginRequest $request)
    {
        [
            'mobile' => $mobile,
            'password' => $password,
        ] = $request->filldata();
        $user = $this->userService->passwordLogin($mobile, $password);
        if (!$user) {
            return $this->error(__('手机号或密码错误'));
        }

        try {
            $token = $this->token($user);
            return $this->data(compact('token'));
        } catch (ServiceException $e) {
            return $this->error($e->getMessage());
        }
    }


    /**
     * @api {post} /api/v2/login/mobile 短信登录
     * @apiGroup Auth
     * @apiVersion v2.0.0
     *
     * @apiParam {String} mobile 手机号
     * @apiParam {String} mobile_code 短信验证码
     *
     * @apiSuccess {Number} code 0成功,非0失败
     * @apiSuccess {Object} data 数据
     * @apiSuccess {String} data.token token
     */
    public function mobileLogin(MobileLoginRequest $request)
    {


        $this->mobileCodeCheck();

        $data = $request->filldata();
        $mobile = $data['mobile'] ?: '';
        $password = $data['password'] ?: '';
        $user = $this->userService->findMobile($mobile);

        if (!$user) {
            // 直接注册
            $user = $this->userService->createWithMobile($mobile, $password, '');
        }

        try {

            $token = $this->token($user);
            return $this->data(compact('token'));
        } catch (ServiceException $e) {

            return $this->error($e->getMessage());
        }
    }

    /**
     * 生成sessionKey
     * @param Request $request
     * @param AuthBus $authBus
     * @return JsonResponse
     * @throws BindingResolutionException
     * @throws InvalidConfigException
     */
    public function miniSession(Request $request, AuthBus $authBus): \Illuminate\Http\JsonResponse
    {


        $miniProgram = WechatMini::getInstance();
        $code = $request->input('code');
        if (!$code) {

            return $this->error(__('参数错误'));
        }
        $info = $miniProgram->auth->session($code);
        if (!isset($info['openid'])) {
            return $this->error(__('错误'));
        }

        $openid = $info['openid'];

        // session_key存入缓存
        $this->cacheService->put(
            get_cache_key(CacheConstant::WECHAT_MINI_SESSION_KEY['name'], $openid),
            $info['session_key'],
            CacheConstant::WECHAT_MINI_SESSION_KEY['expire']
        );

        return $this->data([
            'openid' => $openid,
            'unionid' => $info['unionid'] ?? ''
        ]);
    }


    /**
     * @api {post} /api/v2/login/mini/mobile 微信小程序手机号登录
     * @apiGroup Authn
     * @apiVersion v2.0.0
     *
     * @apiParam {String} openid openid
     * @apiParam {String} iv iv
     * @apiParam {String} encryptedData
     *
     * @apiSuccess {Number} code 0成功,非0失败
     * @apiSuccess {Object} data 数据
     * @apiSuccess {String} data.token token
     */
    public function miniMobileLogin(Request $request, AuthBus $authBus)
    {

        $openId = $request->input('openid');
        $unionId = $request->input('unionid', '');
        $encryptedData = $request->input('encryptedData');
        $iv = $request->input('iv');
        $userInfo = $request->input('userInfo');
        $rawData = $request->input('rawData');
        $signature = $request->input('signature');

        if (!$openId || !$encryptedData || !$iv || !$rawData || !$signature || !$userInfo) {
            return $this->error(__('参数错误'));
        }

        $sessionKey = $this->cacheService->get(get_cache_key(CacheConstant::WECHAT_MINI_SESSION_KEY['name'], $openId));
        if (!$sessionKey) {
            return $this->error(__('sessionKey错误'));
        }

        // 校验签名
        if (sha1($rawData . $sessionKey) !== $signature) {
            return $this->error(__('签名参数错误'));
        }

        $mini = WechatMini::getInstance();

        // 解密获取手机号
        $data = $mini->encryptor->decryptData($sessionKey, $iv, $encryptedData);
        $mobile = $data['phoneNumber'];
        //这里当用微信公众号来登录的时候 初始化一个

        $userId = $authBus->wechatMiniMobileLogin($openId, $unionId, $mobile, $userInfo);
        if (!$userId) {
            return $this->error(__('错误'));
        }

        $user = $this->userService->find($userId,['profile']);

        try {
            $token = $this->token($user);
            return $this->data(compact('token', 'user'));
        } catch (ServiceException $e) {
            return $this->error($e->getMessage());
        }
    }


    /**
     * @api {post} /api/login/mini/open/silent 微信小程序静默授权登录
     * @apiGroup Auth
     * @apiVersion v2.0.0
     *
     * @apiParam {String} openid openid
     * @apiParam {String} iv iv
     * @apiParam {String} encryptedData
     * @apiParam {String} rawData rawData
     * @apiParam {String} signature signature
     *
     * @apiSuccess {Number} code 0成功,非0失败
     * @apiSuccess {Object} data 数据
     * @apiSuccess {String} data.token token
     */
    public function miniSilentLogin(Request $request, AuthBus $authBus)
    {
        $openid = $request->input('openId');
        $raw = $request->input('rawData');
        $signature = $request->input('signature');
        $encryptedData = $request->input('encryptedData');
        $iv = $request->input('iv');

        if (
            !$openid ||
            !$raw ||
            !$signature ||
            !$encryptedData ||
            !$iv
        ) {
            return $this->error(__('参数错误'));
        }

        $sessionKey = $this->cacheService->get(get_cache_key(CacheConstant::WECHAT_MINI_SESSION_KEY['name'], $openid));
        if (!$sessionKey) {
            return $this->error(__('sessionkey没有生成'));
        }

        // 验签
        if (sha1($raw . $sessionKey) !== $signature) {
            return $this->error(__('验签错误'));
        }

        $miniProgram = WechatMini::getInstance();

        // 解密获取用户信息
        $userData = $miniProgram->encryptor->decryptData($sessionKey, $iv, $encryptedData);

        if ($openid !== $userData['openId']) {
            return $this->error(__('openid错误'));
        }

        // unionId
        $unionId = $userData['unionId'] ?? '';
        //静默登录是肯定会存在账户的
        $userId = $authBus->wechatMiniLogin($openid, $unionId);
        if (!$userId) {
            return $this->error(__('错误4'));
        }

        $user = $this->userService->find($userId,['profile']);

        try {
            $token = $this->token($user);

            return $this->data(compact('token', 'user'));
        } catch (ServiceException $e) {
            return $this->error($e->getMessage());
        }
    }




    /**
     * 抖音登录
     * @param Request $request
     * @param AuthBus $authBus
     * @return JsonResponse
     * @throws GuzzleException
     */
    public function douyinSession(Request $request, AuthBus $authBus): \Illuminate\Http\JsonResponse
    {


        $code = $request->input('code');
        $anonymousCode = $request->input('anonymous_code');
        if (!$code && !$anonymousCode) {
            return $this->error(__('参数错误'));
        }
        $client = new Client();
        $response = $client->request('POST', 'https://developer.toutiao.com/api/apps/v2/jscode2session', [
            'json' => [
                'appid' => 'tt7ee2085fe1d300be01',
                'secret' => '94acba404a0dbe8f4f02b3325b8820db56208d8d',
                'code' => $code,
                'anonymous_code' => $anonymousCode,
            ]
        ]);

        $body = $response->getBody()->getContents();
        $info = json_decode($body, true);

        if ($info['err_no'] == 0 && $info['err_tips'] == 'success') {
            $openid = $info['data']['openid'];
        } else {
            return $this->error(__($info['err_tips']));
        }

        // session_key存入缓存
        $this->cacheService->put(
            get_cache_key(CacheConstant::WECHAT_DOUYIN_SESSION_KEY['name'], $openid),
            $info['data']['session_key'],
            CacheConstant::WECHAT_DOUYIN_SESSION_KEY['expire']
        );

        return $this->data([
            'openid' => $openid,
            'unionid' => $info['data']['unionid'] ?? '',
            'anonymous_openid' => $info['data']['anonymous_openid'] ?? '',
        ]);
    }


    /**
     * 抖音小程序用户及昵称登录
     * @param Request $request
     * @param AuthBus $authBus
     * @return JsonResponse
     */
    public function douyinLogin(Request $request, AuthBus $authBus)
    {

        $openId = $request->input('open_id');
        $unionId = $request->input('union_id', '');
        $userInfo = $request->input('user_info');
        if (!$openId || !$unionId || !$userInfo) {
            return $this->error(__('参数错误'));
        }
        $userId = $authBus->douyinLogin($openId, $unionId, $userInfo);
        if (!$userId) {
            return $this->error(__('系统错误'));
        }

        $user = $this->userService->find($userId);

        try {
            $token = $this->token($user);
            return $this->data(compact('token', 'user'));
        } catch (ServiceException $e) {
            return $this->error($e->getMessage());
        }


    }


    /**
     * 抖音手机号一键登录
     * @param Request $request
     * @param AuthBus $authBus
     * @return JsonResponse
     */
    public function douyinMobileLogin(Request $request, AuthBus $authBus)
    {
        $corpId = $request->input('corp_id', 36);
        $openId = $request->input('openid');
        $unionId = $request->input('unionid', '');
        $encryptedData = $request->input('encryptedData');
        $iv = $request->input('iv');

        if (!$openId || !$encryptedData || !$iv) {
            return $this->error(__('参数错误'));
        }

        $sessionKey = $this->cacheService->get(get_cache_key(CacheConstant::WECHAT_DOUYIN_SESSION_KEY['name'], $openId));
        if (!$sessionKey) {
            return $this->error(__('sessionKey错误'));
        }

        $aesKey = base64_decode($sessionKey);
        $aesIV = base64_decode($iv);
        $aesCipher = base64_decode($encryptedData);

        $result = openssl_decrypt($aesCipher, "AES-128-CBC", $aesKey, 1, $aesIV);

        $data = json_decode($result);

        $mobile = $data->phoneNumber;
        //这里当用微信公众号来登录的时候 初始化一个
        $userId = $authBus->douyinMiniMobileLogin($openId, $unionId, $mobile, $corpId, []);
        if (!$userId) {
            return $this->error(__('错误'));
        }

        $user = $this->userService->find($userId);

        try {
            $token = $this->token($user);
            return $this->data(compact('token', 'user'));
        } catch (ServiceException $e) {
            return $this->error($e->getMessage());
        }
    }


    /**
     * @api {post} /api/v2/login/douyin/mobile 微信小程序静默授权登录
     * @apiGroup Auth
     * @apiVersion v2.0.0
     *
     * @apiParam {String} openid openid
     * @apiParam {String} iv iv
     * @apiParam {String} encryptedData
     * @apiParam {String} rawData rawData
     * @apiParam {String} signature signature
     *
     * @apiSuccess {Number} code 0成功,非0失败
     * @apiSuccess {Object} data 数据
     * @apiSuccess {String} data.token token
     */
    public function douyinSilentLogin(Request $request, AuthBus $authBus)
    {
        $openid = $request->input('openid');
        $unionId = $request->input('unionid');
        if (!$openid
        ) {
            return $this->error(__('参数错误'));
        }

        //静默登录是肯定会存在账户的
        $userId = $authBus->douyinMiniLogin($openid, $unionId);
        if (!$userId) {
            return $this->error(__('账户出错'));
        }

        $user = $this->userService->find($userId);

        try {
            $token = $this->token($user);

            return $this->data(compact('token'));
        } catch (ServiceException $e) {
            return $this->error($e->getMessage());
        }
    }


    /**
     * @api {get} /api/v2/login/wechat/oauth 微信公众号授权登录[重定向] 手机登录
     * @apiGroup Auth
     * @apiVersion v2.0.0
     * @apiDescription 登录成功之后会在success_redirect中携带token返回
     *
     * @apiParam {String} success_redirect 成功之后的跳转URL(需要urlencode)
     * @apiParam {String} failed_redirect 失败之后跳转的URL(需要urlencode)
     *
     * @apiSuccess {Number} code 0成功,非0失败
     * @apiSuccess {Object} data 数据
     */
    public function wechatLogin(Request $request)
    {

        $successRedirect = $request->input('success_redirect');
        $failedRedirect = $request->input('failed_redirect');//todo  这里没有处理失败的链接
        $redirectUrl = route('api.v2.login.wechat.callback');
        $redirectUrl = url_append_query($redirectUrl, ['s_url' => urlencode($successRedirect), 'f_url' => urlencode($failedRedirect)]);
        $redirectUrl = Wechat::getInstance()->oauth->redirect($redirectUrl);
        return redirect($redirectUrl);

    }


    /**
     * 微信公众号授权登录[回调]
     * @param Request $request
     * @param AuthBus $authBus
     * @return Application|RedirectResponse|Redirector
     * @throws GuzzleException
     * @throws BindingResolutionException
     * @throws AuthorizeFailedException
     */
    public function wechatLoginCallback(Request $request, AuthBus $authBus)
    {
        $successRedirectUrl = urldecode($request->input('s_url', ''));
        $failedRedirectUrl = urldecode($request->input('f_url'));
        $code = urldecode($request->input('code'));
        $user = Wechat::getInstance()->oauth->userFromCode($code);
        if (!$user) {
            return redirect(url_append_query($failedRedirectUrl, ['msg' => __('错误')]));
        }

        $originalData = $user->getRaw();
        $openid = $originalData['openid'];
        $unionId = $originalData['unionid'] ?? '';

        $userId = $authBus->wechatLogin($openid, $unionId, $originalData);
        $user = $this->userService->find($userId);

        try {
            $token = $this->token($user);
            return redirect(url_append_query($successRedirectUrl, ['token' => $token]));
        } catch (ServiceException $e) {
            return redirect(url_append_query($failedRedirectUrl, ['msg' => $e->getMessage()]));
        }
    }


    /**
     * @api {get} /api/v2/login/wechatScan PC微信扫码登录[二维码]
     * @apiGroup Auth
     * @apiVersion v2.0.0
     *
     * @apiSuccess {Number} code 0成功,非0失败
     * @apiSuccess {Object} data 数据
     * @apiSuccess {String} data.code 随机值
     * @apiSuccess {String} data.image 图片内容
     */
    public function wechatScan(BusinessState $businessState)
    {


        // 生成登录二维码
        $code = Str::random(10);
        $image = wechat_qrcode_image($code);

        return $this->data([
            'code' => $code,
            'image' => $image,
        ]);
    }

    /**
     * @api {get} /api/v2/login/wechatScan/query PC微信扫码登录[结果查询]
     * @apiGroup Auth
     * @apiVersion v2.0.0
     *
     * @apiParam {String} code 随机值
     *
     * @apiSuccess {Number} code 0成功,非0失败
     * @apiSuccess {Object} data 数据
     * @apiSuccess {Number} data.status 结果[1:登录成功,0:失败]
     * @apiSuccess {String} data.token token[登录成功返回]
     */
    public function wechatScanQuery(Request $request, AuthBus $authBus, WechatScanBus $wechatScanBus)
    {
        $code = $request->input('code');
        if (!$code) {
            return $this->error(__('参数错误'));
        }

        $userId = $wechatScanBus->getLoginUserId($code);
        if (!$userId) {
            return $this->data(['success' => -1]);
        }

        if ($userId === AuthBus::ERROR_CODE_BIND_MOBILE) {
            return $this->data([
                'success' => 0,
                'action' => 'bind_mobile',
                'code' => $code,
            ]);
        }

        $token = $authBus->tokenLogin($userId, FrontendConstant::LOGIN_PLATFORM_PC);

        return $this->data([
            'success' => 1,
            'token' => $token,
        ]);
    }


    public function logout()
    {
        Auth::guard(FrontendConstant::API_GUARD)->logout();
        return $this->success();
    }

    /**
     * 公众号鉴权
     * @param Request $request
     * @return JsonResponse
     * @throws BindingResolutionException
     * @throws GuzzleException
     * @throws InvalidArgumentException
     * @throws InvalidConfigException
     * @throws RuntimeException
     * @throws \Psr\SimpleCache\InvalidArgumentException
     */
    public function wechatConfig(Request $request)
    {
        $url = $request->input('url');
        $app = Wechat::getInstance();
        // $url = substr($url, 0, stripos($url, "#"));
        $app->jssdk->setUrl($url);
        $buildConfig = $app->jssdk->buildConfig(array(), true, false, false);
        return $this->successData(compact('buildConfig'));


    }


}
